package com.centriole.service.workflow.service.impl;

import com.centriole.service.workflow.common.Constant;
import com.centriole.service.workflow.common.QueryVo;
import com.centriole.service.workflow.common.StringConventor;
import com.centriole.service.workflow.dao.InstanceHistoryMapper;
import com.centriole.service.workflow.dao.ProInstanceMapper;
import com.centriole.service.workflow.dao.ProcessMapper;
import com.centriole.service.workflow.entity.*;
import com.centriole.service.workflow.entity.Process;
import com.centriole.service.workflow.enums.CancelTask;
import com.centriole.service.workflow.enums.FlowNode;
import com.centriole.service.workflow.enums.StateType;
import com.centriole.service.workflow.service.*;
import com.centriole.service.workflow.service.parse.BpmnObject;
import com.centriole.service.workflow.service.vo.CompleteTaskRequest;
import com.centriole.service.workflow.util.*;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 工作流核心操作
 */
@Service
public class ProcessServiceImpl implements ProcessService {
	@Resource
	private ProcessMapper processMapper;
	@Resource
	private ProInstanceMapper proInstanceMapper;
	@Resource
	private ActManagerService actManagerService;
	@Resource
	private ProNodeService proNodeService;
	@Resource
	private ProParameterService proParameterService;
	@Resource
	private NoticeService noticeService;
	@Resource
	private InstanceHistoryMapper instanceHistoryMapper;
	
	@Resource private FlowTraceService flowTraceService;
	
	@Resource private WorkPageService workPageService;
	
	@Resource private FunctionService functionService;
	
	public void insert(Process process) {
		processMapper.insert(process);
	}

	/**
	 * 编辑流程，通过传入的process的id是否为空，来执行 新增还是修改 操作
	 * @param process
	 * @return
	 * @throws Exception
	 */
	public String editProcess(Process process) throws Exception {
		//check对象字段检查
		AjaxResult check = check(process);
		if(check.isError){
			throw new Exception(check.message);
		}
		//在点击部署的时候才开始bpmn解析
//		if(CommonUtil.isNotEmpty(process.getBapBpmn())){
//			parseBpmn(process);
//		}

		if(CommonUtil.isEmpty(process.getId())){
			//新增
			String id = CommonUtil.getUuid();
			process.setId(id);
			process.setVersion(0);
			process.setProcessKey(process.getProcessKey());
			process.setProcessName(process.getProcessName());
			process.setState(StateType.NODEPLOY.statusCode());
			process.setCreateDate(new Date());
			processMapper.insert(process);
			return	id;
		}else{
			Process p = processMapper.selectById(process.getId());
			if(!StateType.NODEPLOY.statusCode().equals(p.getState())){
				//已部署过，更新版本，新添加一条数据
				process.setId(CommonUtil.getUuid());
				process.setVersion(p.getVersion()+1);
				process.setState(StateType.NODEPLOY.statusCode());
				process.setCreateDate(new Date());
				processMapper.insert(process);
			}else{
				//修改
				process.setVersion(p.getVersion()+1);
				processMapper.update(process);
				//重新解析bpmn
				parseBpmn(process);	
				
			}
			return p.getId();
		}
	}

	/**
	 * 根据 前端传过来的process的json数据 解析成符合 工作流bpmn规范的xml格式字符串
	 * @param process
	 */
	private void parseBpmn(Process process) {
		StringBuffer bpmn = new StringBuffer();
		//start
		bpmn.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
		bpmn.append("<definitions xmlns=\"http://www.omg.org/spec/BPMN/20100524/MODEL\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:activiti=\"http://activiti.org/bpmn\" xmlns:bpmndi=\"http://www.omg.org/spec/BPMN/20100524/DI\" xmlns:omgdc=\"http://www.omg.org/spec/DD/20100524/DC\" xmlns:omgdi=\"http://www.omg.org/spec/DD/20100524/DI\" typeLanguage=\"http://www.w3.org/2001/XMLSchema\" expressionLanguage=\"http://www.w3.org/1999/XPath\" targetNamespace=\"http://www.activiti.org/test\">");
		bpmn.append("<process id=\""+process.getProcessKey()+"\" name=\""+process.getProcessName()+"\" isExecutable=\"true\">");
		//节点和连接线xml
		String nodesXml = parseNodeAndLine(process);
		bpmn.append(nodesXml);

		//end
		bpmn.append("</process>");
		bpmn.append("</definitions>");
		process.setBpmn(bpmn.toString());
		System.out.println(bpmn.toString());
	}


	/**
	 * 解析出节点信息
	 * v2: 2018/1/3 添加BpmnObject类 辅助解析
	 * v3: 2018/1/4 proNode需要设置 终止、撤回、挂起 操作标识符 
	 * v4: 2018/1/10 setCode()现在修改为 id + type 的格式,比如"demo_node_3-multiTaskOr",并添加流程变量的解析
	 * @param process
	 * @return 节点xml
	 */
	private String parseNodeAndLine(Process process) {
		String userId = process.getCreatorId();		//获得该流程的创建人id
		//TODO:新增一个解析java对象
		BpmnObject bpmnObject = BpmnObject.parseToBpmnObject(process);
		//TODO:-------------------------------------------------------------------
		String bapbpmn = process.getBapBpmn();
		JSONObject bpmnJson = JSONObject.fromObject(bapbpmn);
		StringBuffer bpmn = new StringBuffer();
		JSONObject nodes = bpmnJson.getJSONObject("nodes");
		JSONObject properties = bpmnJson.getJSONObject("properties");
		proNodeService.deleteByProcessId(process.getId());
		noticeService.deleteByProcessId(process.getId());
		for(Object key:nodes.keySet()){
			JSONObject node = nodes.getJSONObject(key.toString());
			JSONObject property = properties.getJSONObject(key.toString());
			if(FlowNode.START.equals(node.getString(FlowNode.NODE_TYPE))){
				bpmn.append("<startEvent id=\""+key.toString()+"\" name=\""+node.getString(FlowNode.NODE_NAME)+"\"></startEvent>");
			}else if(FlowNode.USER_TASK.equals(node.getString(FlowNode.NODE_TYPE))){
				//查询执行人
				String ids = properties.getJSONObject(key.toString()).getString(FlowNode.FKID);
				if(FlowNode.FKUSER.equals(properties.getJSONObject(key.toString()).getString(FlowNode.FKTYPE))){
					bpmn.append("<userTask id=\""+key.toString()+"\" name=\""+node.getString(FlowNode.NODE_NAME)+"\" activiti:candidateUsers=\""+BpmnObject.isCurrentUserAndreplace(ids)+"\"></userTask>");
				}else if(FlowNode.FKGROUP.equals(properties.getJSONObject(key.toString()).getString(FlowNode.FKTYPE))){
					bpmn.append("<userTask id=\""+key.toString()+"\" name=\""+node.getString(FlowNode.NODE_NAME)+"\" activiti:candidateGroups=\""+ids+"\"></userTask>");
				}
			
				addProNode(key,properties,property,node,process);
				
			}else if( FlowNode.MULTI_TASK.equals(node.getString(FlowNode.NODE_TYPE))  ){
				JSONObject tasks = properties.getJSONObject(key.toString()).getJSONObject(FlowNode.NODE_TASKS);
				for(Object childNodeKey:tasks.keySet()){
					JSONObject childNode = tasks.getJSONObject(childNodeKey.toString());
					//向 'pro_node'表  新增多任务的子节点
					if(CommonUtil.isNotEmpty(properties.getJSONObject(key.toString()))){
						Object view = childNode.get(FlowNode.NODE_VIEWPAGEID);
						Object edit = childNode.get(FlowNode.NODE_EDITPAGEID);
						Object function = childNode.get(FlowNode.NODE_FUNCTIONID);
						ProNode proNode = new ProNode();
						proNode.setId(CommonUtil.getUuid());
						proNode.setProcessId(process.getId());
						proNode.setCode(key.toString()  + "-" +FlowNode.MULTI_TASK_CHILD );	//必定是 多任务子任务 ， 不用从json中获取
						proNode.setNodeName( node.getString(FlowNode.NODE_NAME ) + "-" +childNode.getString(FlowNode.NODE_NAME));
						proNode.setViewId(view==null?null:view.toString());
						proNode.setEditId(edit==null?null:edit.toString());
						proNode.setFunctionId(function==null?null:function.toString());
						proNode.setState(StateType.NORMAL.statusCode());
						proNode.setCreateDate(new Date());
						proNode.setAllowStop( properties.get(FlowNode.ALLOW_STOP)==null?FlowNode.SWITCH_OFF:FlowNode.SWITCH_ON );
						proNode.setAllowRecall( properties.get(FlowNode.ALLOW_RECALL)==null?FlowNode.SWITCH_OFF:FlowNode.SWITCH_ON );
						proNode.setAllowHang(  properties.get(FlowNode.ALLOW_HANG)==null?FlowNode.SWITCH_OFF:FlowNode.SWITCH_ON );
						proNode.setCreatorId(userId);
						proNode.setModifierId(userId);
						proNodeService.insertNode(proNode);
					}
				}

			}else if(FlowNode.EXAMINE.equals(node.getString(FlowNode.NODE_TYPE))){
				addProNode(key,properties,property,node,process);
			}else if(FlowNode.END.equals(node.getString(FlowNode.NODE_TYPE))){
				bpmn.append("<endEvent id=\""+key.toString()+"\" name=\""+node.getString(FlowNode.NODE_NAME)+"\"></endEvent>");
			}
		}
		
		//解析多任务节点、审批节点
		bpmn.append( bpmnObject.parseComplexNode() );
		//解析连接线
		bpmn.append( bpmnObject.parseLine() );
		
		//向'pro_parameter'表中添加流程变量的数据
//		for (ProParameter param : bpmnObject.getParams() ) {
//			proParameterService.insertParameter(param);
//		}
		return bpmn.toString();
	}
	
	/**
	 * 【私有方法】【parseNodeAndLine(...)中会调用此方法】添加ProNode到数据库中
	 * @param key
	 * @param properties
	 * @param property
	 * @param node
	 * @param process
	 */
	private void addProNode(Object key , JSONObject properties , JSONObject property , JSONObject node, Process process) {
		//新增节点
		if(CommonUtil.isNotEmpty(properties.getJSONObject(key.toString()))){
			Object view = properties.getJSONObject(key.toString()).get(FlowNode.NODE_VIEWPAGEID);
			Object edit = properties.getJSONObject(key.toString()).get(FlowNode.NODE_EDITPAGEID);
			Object function = properties.getJSONObject(key.toString()).get(FlowNode.NODE_FUNCTIONID);
			ProNode proNode = new ProNode();
			proNode.setId(CommonUtil.getUuid());
			proNode.setProcessId(process.getId());
			proNode.setCode(key.toString() + "-" +node.getString(FlowNode.NODE_TYPE));
			proNode.setNodeName(node.getString(FlowNode.NODE_NAME));
			proNode.setViewId(view==null?null:view.toString());
			proNode.setEditId(edit==null?null:edit.toString());
			proNode.setFunctionId(function==null?null:function.toString());
			proNode.setState(StateType.NORMAL.statusCode());
			proNode.setCreateDate(new Date());
			proNode.setCreatorId(process.getCreatorId());
			proNode.setModifierId(process.getModifierId());
			proNode.setCreatorId(process.getCreatorId());
			proNode.setModifierId(process.getModifierId());
			proNode.setAllowStop( property.get(FlowNode.ALLOW_STOP)==null?FlowNode.SWITCH_OFF:FlowNode.SWITCH_ON );
			proNode.setAllowRecall( property.get(FlowNode.ALLOW_RECALL)==null?FlowNode.SWITCH_OFF:FlowNode.SWITCH_ON );
			proNode.setAllowHang(  property.get(FlowNode.ALLOW_HANG)==null?FlowNode.SWITCH_OFF:FlowNode.SWITCH_ON);
		//新增通知
			if(null!=properties.getJSONObject(key.toString()).get(FlowNode.NOTICES)){
				JSONArray notices = properties.getJSONObject(key.toString()).getJSONArray(FlowNode.NOTICES);
				for(int noindex=0;noindex<notices.size();noindex++){
					JSONObject jsonNotice = notices.getJSONObject(noindex);
					Notice notice = new Notice();
					notice.setId(CommonUtil.getUuid());
					notice.setProcessId(process.getId());
					notice.setProNodeId(proNode.getId());
					notice.setNoticeTitle(jsonNotice.getString(FlowNode.NOTICE_TITLE));
					notice.setNoticeCount(jsonNotice.getString(FlowNode.NOTICE_CONTENT));
					notice.setState(StateType.NORMAL.statusCode());
					notice.setCreateDate(new Date());
					notice.setReceiveType(jsonNotice.getString(FlowNode.RADIOID));
					notice.setNoticeReceive(jsonNotice.getString(FlowNode.FKID));
					notice.setCreatorId(process.getCreatorId());
					notice.setModifierId(process.getModifierId());
					if("Email".equals(jsonNotice.getString(FlowNode.NOTICE_NOTICEWAY))){
						notice.setNoticeType("0");
					}
					noticeService.insertNotice(notice);
				}
			}
		proNodeService.insertNode(proNode);
		}
	}

	/**
	 * 【私有方法】检查process的输入是否合法
	 * @param process
	 * @return
	 */
	private AjaxResult check(Process process) {
		if(CommonUtil.isEmpty(process.getProcessKey())){
			return AjaxResult.Error("流程定义key不能为空");
		}else{
			List<Process> list = processMapper.selectListByProcessKey(process.getProcessKey());
			if(CommonUtil.isNotEmpty(list)&&CommonUtil.isEmpty(process.getId())){
				return AjaxResult.Error("流程定义key已存在");
			}
		}
		if(CommonUtil.isEmpty(process.getProcessName())){
			return AjaxResult.Error("流程名称不能为空");
		}
		return AjaxResult.Success();
	}

	/**
	 * 通过 流程的名称 来查询流程
	 * @param processName
	 * @return
	 */
	public List<Process> select(String processName) {
		if(null==processName)
			processName = "";
		List<Process> list= processMapper.select(processName.trim());
		for(Process p:list){
			p.setCreateDateStr(CommonUtil.getymdhms(p.getCreateDate()));
			if(CommonUtil.isNotEmpty(p.getEditDate()))
				p.setEditDateStr(CommonUtil.getymdhms(p.getEditDate()));
		}
		return list;
	}


	/**
	 * 查询 所有 有效的 流程实例
	 * @return
	 */
	public List<Process> listAll(){
		List<Process> list=processMapper.selectAll();
		for(Process p:list){
			p.setCreateDateStr(CommonUtil.getymdhms(p.getCreateDate()));
			if(CommonUtil.isNotEmpty(p.getEditDate()))
				p.setEditDateStr(CommonUtil.getymdhms(p.getEditDate()));
		}
		return list;
	}

	/**
	 * 逻辑删除一个流程 ， 字段'state'改为 '9' 表示删除
	 * @param processId
	 */
	public void logicDelete(String processId) {
		processMapper.logicDelete(processId);
		// TODO 逻辑删除其他关联表
		//流程节点表
		proNodeService.logicDeleteByProcessId(processId);
		//流程参数表
		proParameterService.logicDeleteByProcessId(processId);
	}

	/**
	 * 真正地从数据库表总删除一个 流程
	 * @param processId
	 */
	public void delete(String processId) {
		processMapper.delete(processId);
		// TODO 删除其他关联表
		//流程节点表
		proNodeService.deleteByProcessId(processId);
		//流程参数表
		proParameterService.deleteByProcessId(processId);
	}

	/**
	 * 通过流程id 来查询 流程
	 * @param processId
	 * @return
	 */
	public Process selectById(String processId) {
		Process process = processMapper.selectById(processId);
		return process;
	}

	/**
	 * 根据流程key来查询
	 * @param processKey
	 * @return
	 */
	public Process selectByKey(String processKey) {
		Process process = processMapper.selectByProcessKey(processKey);
		return process;
	}

	/**
	 * 获取所有部署成功的流程
	 * @return
	 */
	public List<Process> getDeployedProcess() {
		List<Process> list = processMapper.getDeployedProcess();
		return list;
	}

	/**
	 * 	通过流程id来部署该流程
	 * @param processId
	 * @throws Exception
	 */
	public void deploy(String processId) throws Exception {
		Process process = processMapper.selectById(processId);
		if(!StateType.NODEPLOY.statusCode().equals(process.getState())){
			//该流程已部署过
			throw new Exception(process.getProcessKey()+"已部署过！");
		}else{
			if(CommonUtil.isEmpty(process.getBapBpmn())){
				throw new Exception(process.getProcessKey()+"未配置流程图！");
			}
			try {
				if(StringUtils.isBlank( process.getBpmn() )) {
					//延迟bpmn解析
					if(CommonUtil.isNotEmpty(process.getBapBpmn())){
						parseBpmn(process);
					}
				}
				//部署流程
				actManagerService.deploy(process.getProcessKey(), process.getBpmn(), ActManagerService.STRING);
				//根据processKey修改已部署流程状态
				Process processLod = processMapper.selectByProcessKey(process.getProcessKey());
				if(null!=processLod){
					processLod.setState(StateType.NODEPLOY.statusCode());
					processMapper.update(processLod);
				}
				//修改流程状态
				process.setState(StateType.DEPLOYED.statusCode());
				processMapper.update(process);
			} catch (Exception e) {
				throw e;
			}
		}
	}

	/**
	 * 通过一个流程定义key来停止一个流程
	 * @param key
	 */
	public void stop(String key) {
		Process processLod = processMapper.selectByProcessKey(key);
		if(null!=processLod){
			processLod.setState(StateType.STOP.statusCode());
			processMapper.update(processLod);
		}
	}

	/**
	 * 通过一个流程定义key来运行一个流程？？？
	 * @param key
	 */
	public void run(String key) {
		Process processLod = processMapper.selectByProcessKey(key);
		if(null!=processLod){
			//状态控制    ps:只改了下状态？？？
			processLod.setState(StateType.DEPLOYED.statusCode());
			processMapper.update(processLod);
		}
	}

	/**
	 * Service层  用户发起一个流程
	 * @param proInstance
	 * @param request
	 * @return
	 */
	@Transactional
	@Override
	public String start(ProInstance proInstance , HttpServletRequest request) {
		try {
			proInstance.setId(CommonUtil.getUuid());
			//除了 以下三个参数外， 如果还有其他参数将视为流程变量
			Map<String, Object> variables = request.getParameterMap().entrySet().parallelStream().filter(
					e -> !"startUserId".equals( e.getKey()) &&			//流程发起人
							!"processId".equals(e.getKey()) &&				//流程id
							!"appId".equals(e.getKey()))							//调用服务的应用名【虽然这里叫 'appId'】
					.collect(Collectors.toMap(e->e.getKey() , e->e.getValue()[0]));
			String startUserId = request.getParameter("startUserId");
			String startUserName = request.getParameter("startUserName");
			variables.put("currentUser", StringUtils.isBlank( startUserId )?UserUtils.currentUserInfo().getEmpId()+"" : startUserId );
			//发起流程，如果成功会返回一个流程实例id
			String instanceId = actManagerService.start(proInstance.getProcessId(), proInstance.getId(), variables, StringUtils.isBlank( proInstance.getStartUserId()  )?UserUtils.currentUserInfo().getEmpId()+"" : proInstance.getStartUserId());
			//封装1个ProInstance实体  并保存到 'pro_instance'表中
			proInstance.setInstanceId(instanceId);
			proInstance.setProcessId( processMapper.selectByProcessKey(proInstance.getProcessId()) .getId());
			proInstance.setApplyUser( StringUtils.isBlank(startUserName)?"佚名":startUserName );
			proInstance.setState(StateType.APPROVAL_RUN.statusCode());
			proInstance.setCreateDate(new Date());
			proInstance.setRemark( StringUtils.isBlank(  proInstance.getRemark())?Constant.DEFALUT_REMARK : proInstance.getRemark());
			proInstance.setCreatorId(StringUtils.isBlank( proInstance.getCreatorId() )?UserUtils.currentUserInfo().getEmpId()+"":proInstance.getCreatorId());
			proInstance.setModifierId(proInstance.getCreatorId());

			proInstanceMapper.insert(proInstance);
			
			//默认记录一条 流程进度 日志，发起成功后就要记录一条
			InstanceHistory history = new InstanceHistory();
			history.setId(CommonUtil.getUuid());
			history.setProcessId(proInstance.getProcessId());
			history.setInstanceId(instanceId);
			history.setTaskName("流程启动");
			history.setCompleteUser(UserUtils.currentUserInfo().getName());
			history.setCompleteState("1");
			history.setCreateDate(new Date());
			history.setRemark(proInstance.getRemark());
			history.setLevel(FlowNode.HISTORY_LEVEL_A);
			instanceHistoryMapper.insert(history);

			return instanceId;
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
	}

	/**
	 *  通用 应用名 和  用户id 查询 该用户可以办理的关于该应用的流程任务
	 * @param appName	对外提供服务时的应用名
	 * @param userId		用户id
	 * @return
	 */
	@Override
	public List<TaskModel> getTask( String appName , String userId ) {
		List<String> instanceRecoder = new ArrayList<>(30);		//用于记录任务的流程实例id,初始化大小为30,主要在遍历task时,判断task的流程实例id是否已经出现过
		List<String> roles = new ArrayList<>();
		List<Task> taskList = actManagerService.findToDoTasks(null, StringUtils.isBlank(userId)?UserUtils.currentUserInfo().getEmpId()+"":userId, roles);
		//【1】如果appName不为空，则对 taskList进行筛选，与appName匹配的Task留下
		if(StringUtils.isNoneBlank( appName )){
				taskList = taskList.stream()
						.filter( e-> {
						ProInstance proInstance = proInstanceMapper.selectByInstanceId(e.getProcessInstanceId());
						if(proInstance!=null){
							System.out.println( appName +": "+ proInstance.getInstanceId() +"(" +proInstance.getAppName()+")");
							return appName.equalsIgnoreCase( proInstance.getAppName()) ;
						}
						return false;
					})
					.collect(Collectors.toList());
		}
		List<TaskModel> list = new ArrayList<TaskModel>();
		//【2】循环遍历每个Task，将其包装成 前端vo对象 TaskModel
		for(Task t:taskList){
			TaskModel tm = new TaskModel();
			tm.setInstanceId(t.getProcessInstanceId());
			tm.setTaskId(t.getId());
			ProInstance p = proInstanceMapper.selectByInstanceId(t.getProcessInstanceId());
			if(p == null) 	continue;
			//		System.err.println("【流程id: " +t.getId() + " 在业务表 'pro_instance' 中 不存在！】");
			//尝试获取 任务类型
			ProNode proNode = getProNodeOfCurrentTask(t.getProcessDefinitionId(), t.getProcessInstanceId() ,t );
			//如果获取不到任务类型，则将其视为无效任务，跳过接下来处理
			if(proNode==null) 	continue;
			//		System.err.println("【流程id: " +t.getId() + " 在业务表 'pro_node' 中 不存在！】");
			//一个实例id只能有一个代表的任务
			if ( instanceRecoder.contains(tm.getInstanceId()) ) {		//一般的单任务是不会出现一个流程实例多个任务的情况，这里主要是检测审批节点,会出现2个子任务(同意和驳回这2个子任务)
				continue;
			}
			if(proNode.getTaskType().equals( FlowNode.MULTI_TASK_CHILD )) {		//对多任务节点不检查流程实例id重复,并且taskName追加旗下子任务的名称
				tm.setTaskName( FlowNode.MULTI_TASK_PREFIX + proNode.getNodeName());		//添加一个"(多任务)"前缀表示
			}else {
				tm.setTaskName(proNode.getTaskType().equals(FlowNode.EXAMINE)?FlowNode.EXAMINE_PREFIX+proNode.getNodeName():proNode.getNodeName());	//如果是审批节点添加一个"(审批)"前缀
				instanceRecoder.add(tm.getInstanceId());
			}	
			//如果配置表单页面
			if(proNode.getViewId()!=null) {
		//		String viewPageUrl = workPageService.selectById(proNode.getViewId()).getPageUrl() + getModelId(Integer.valueOf( tm.getInstanceId() ));
				//替换rawUrl 带有 ${} 的占位符
				String rawUrl = workPageService.selectById(proNode.getViewId()).getPageUrl();
				tm.setViewPageUrl(setPlaceHolderViewPage(rawUrl ,t.getProcessInstanceId() ));
				/*
				String viewPageUrl = StringConventor.placeHolder(
							rawUrl,
							Constant.VIEWPAGE_PREFIX,
							Constant.VIEWPAGE_SUFFIX,
							FlowVariable.toMap(processMapper.selectVariablesByInstanceId(t.getProcessInstanceId())));
				tm.setViewPageUrl( viewPageUrl );
				*/
			}
			tm.setTaskType( proNode.getTaskType() );
			tm.setStartUser(p.getApplyUser());
			tm.setFlowRemark(p.getRemark());
			tm.setProcessName(p.getProNameStr());
			list.add(tm);
		}
		return list;
	}

	/**
	 * 查询出 该用户 已经办理过的(该用户参与过该流程) 流程任务
	 * @param vo
	 * @return
	 */
	@Override
	public List<TaskModel> getDeal(QueryVo vo) {
		String userId = StringUtils.isBlank( vo.getUserId()  )?UserUtils.currentUserInfo().getEmpId()+"":vo.getUserId();
		List<HistoricProcessInstance> taskList = actManagerService.findProcessedInstances( userId , "all", null);
		List<TaskModel> list = new ArrayList<TaskModel>();

		//一边遍历一遍 筛选出符合要求的数据
		for(HistoricProcessInstance i:taskList){
			//对流程实例 的 appName 进行筛选，留下与QueryVo中的appName相同的
			ProInstance proInstance = proInstanceMapper.selectByInstanceId(i.getSuperProcessInstanceId());
			if( StringUtils.isNoneBlank(vo.getAppName()) ){
				//如果QueryVo对象中的appName不为空 并且 流程实例的appName不符就跳过
				if( !vo.getAppName().equalsIgnoreCase( proInstance.getAppName() ) ){
					continue;
				}
			}
			TaskModel tm = new TaskModel();
			tm.setBusinessKey(i.getBusinessKey());
			tm.setVariables(i.getProcessVariables());
	
			tm.setStartTime(CommonUtil.getymdhms(i.getStartTime()));
			tm.setInstanceId(i.getId());
			tm.setCompleteRemark(null==i.getProcessVariables().get("remork")?null:i.getProcessVariables().get("remork").toString());
			ProInstance p = proInstanceMapper.selectById(i.getBusinessKey());
			tm.setStartUser(p.getApplyUser());
			tm.setProcessName(p.getProNameStr());
			tm.setProState(p.getState());
			tm.setFlowRemark(p.getRemark());
			//过滤到 撤回、挂起、激活等操作的操作记录
			List<InstanceHistory> historyList = instanceHistoryMapper.selectByInstanceIdOderByDesc(p.getInstanceId()).parallelStream().filter(e->FlowNode.HISTORY_LEVEL_A.equals(e.getLevel()) && !FlowNode.startNodeName.equals(e.getTaskName())).collect(Collectors.toList());
			if(historyList.size()==0) continue;
				
			//如果有表单页面则去获取pageUrl
			List<ProNode> nodes =  proNodeService.selectByProcessId(historyList.get(0).getProcessId());
			nodes = nodes.parallelStream().filter(e->historyList.get(0).getCode().equals(e.getCode())).collect(Collectors.toList());
			if(nodes!=null && nodes.size() >=1 ) {
				ProNode proNode = nodes.get(0);
				if(proNode.getViewId()!=null) {
					String realViewPage = setPlaceHolderViewPage(workPageService.selectById(proNode.getViewId()).getPageUrl(), p.getInstanceId());
					tm.setViewPageUrl( realViewPage );
				}	
			}	
			tm.setTaskName( (historyList.get(0).getWrappedTaskLogicalName() ));
			tm.setNodeId( historyList.get(0).getTaskNodeId());
			tm.setTaskLogicalName( historyList.get(0).getTaskLogicalName() );	
			tm.setCompleteRemark(historyList.get(0).getActualRemark());
			tm.setLastCompleteTime(CommonUtil.getymdhms(historyList.get(0).getCreateDate()));
			tm = setAllowableOperator(i,tm);
			
			list.add(tm);
		}
		return list;
	}

	/**
	 *  查询出所有关于我的发起的流程,比如 "我的办理(进行中)、我的办理(已结束)"
	 * @param isFinished		'进行中' or '已结束'
	 * @param vo		查询对象
	 * @return
	 */
	@Override
	public List<TaskModel> getMyProcess(String isFinished , QueryVo vo) {
		String userId = vo.getUserId();
		List<HistoricProcessInstance> instanceList = actManagerService.findApplyInstaces(StringUtils.isBlank(userId)?UserUtils.currentUserInfo().getEmpId()+"" : userId, isFinished, null);
		List<TaskModel> list = new ArrayList<TaskModel>();

		//一边遍历一遍 筛选出符合要求的数据
		for(HistoricProcessInstance i:instanceList){
			//对流程实例 的 appName 进行筛选，留下与QueryVo中的appName相同的
			ProInstance proInstance = proInstanceMapper.selectByInstanceId(i.getSuperProcessInstanceId());
			if( StringUtils.isNoneBlank(vo.getAppName()) ){
				//如果QueryVo对象中的appName不为空 并且 流程实例的appName不符就跳过
				if( proInstance!=null && !vo.getAppName().equalsIgnoreCase( proInstance.getAppName() ) ){
					continue;
				}
			}

			TaskModel tm = new TaskModel();
			tm.setBusinessKey(i.getBusinessKey());
			tm.setStartUser(i.getStartUserId());
			tm.setVariables(i.getProcessVariables());
			tm.setStartTime(CommonUtil.getymdhms(i.getStartTime()));
			tm.setInstanceId(i.getId());
			tm.setCompleteRemark(null==i.getProcessVariables().get("remork")?null:i.getProcessVariables().get("remork").toString());
		//	tm.setViewPageUrl(proInstanceMapper);
			ProInstance p = proInstanceMapper.selectById(i.getBusinessKey());
			if(p == null) {
		//		System.out.println("【流程id: " +i.getId() + " 在业务表中 不存在！】");
				continue;
			}		
			if("9".equals( p.getState() )) continue;		//9表示该流程被发起人终止了,直接跳过
			tm.setProcessName(p.getProNameStr());
			tm.setProState(p.getState());
			tm.setFlowRemark(p.getRemark());
			tm = setAllowableOperator(i,tm);
			
			list.add(tm);
		}
		return list;
	}
	
	/**
	 * 通过流程定义id和流程实例id 获取 业务表中对应的节点实体实例
	 * @param processDefinitionId		流程定义id
	 * @param instanceId			流程实例id
	 * @return	ProNode对象
	 */
	private ProNode getProNodeOfCurrentTask(String processDefinitionId, String instanceId , Task task) {
		String processDefinitionKey =processDefinitionId.split(":")[0];
		Process process = null;			//activiti框架整合自定义对象
		List<Process> processes = processMapper.selectListByProcessKey(processDefinitionKey);
		if(processes != null && processes.size() > 0) {
			process = processes.get(0);
		}
		if(process == null) {
			System.out.println( " \"" + processDefinitionKey + "\" 没有这个流程定义key");
			return null;
		}
		List<ProNode> nodes = proNodeService.selectByProcessId(process.getId());
		List<ProNode> filterNodes = nodes.parallelStream().filter(e->e.getNodeNameBySplit().equals(task.getName())).collect(Collectors.toList());
		//:如果过滤之后为空，那么尝试将其作为 审批节点 查找
		String taskType = null;
		if(filterNodes.size() == 0) {
			 taskType = task.getTaskDefinitionKey().split("-").length >= 2? task.getTaskDefinitionKey().split("-")[1]:null;
			 if (FlowNode.EXAMINE_AGREE_ENGLISH.equals(taskType) || FlowNode.EXAMINE_REJECT_ENGLISH.equals(taskType)){
				 filterNodes= nodes.parallelStream().filter(e->e.getCode().equals(e.getNodeId()+"-"+FlowNode.EXAMINE)).collect(Collectors.toList());
			 }
		}
		
		if(filterNodes == null || filterNodes.size() == 0 || filterNodes.size() == 0 ) return null;
		return filterNodes.get(0);
	}

	/**
	 * 【私有方法】【getDeal(...) 和 getMyProcess(...) 都会调用此方法】
	 *  当前流程任务节点 对应的流程图的节点 是否能被挂起、终止、撤回， 并将状态 设置到 前端对象TaskModel 中
	 * @param i
	 * @param tm
	 * @return
	 */
	private TaskModel setAllowableOperator(HistoricProcessInstance i, TaskModel tm) {
		//TODO:这里查出对应任务节点的允许操作(挂起、终止、撤回)标志位 , 将值封装到tm对象中
		String processDefinitionKey = i.getProcessDefinitionId().split(":")[0];
		String instanceId = i.getId();	//该任务所在流程实例的 流程id
		String nodeCode = null;		//任务名
		Process process = null;			//activiti框架整合自定义对象
		List<Process> processes = processMapper.selectListByProcessKey(processDefinitionKey);
		if(processes != null && processes.size() > 0) {
			process = processes.get(0);
		}
		if(process == null) {
			System.out.println( " \"" + processDefinitionKey + "\" 没有这个流程定义key");
			return tm;
		}
		List<ProNode> nodes = proNodeService.selectByProcessId(process.getId());
		List<Task> tasks = actManagerService.findTasksByInstanceId(instanceId);
		//TODO: 可能会存在问题，要查询的是 任务,1个多任务节点会有多个任务
		if(tasks != null && tasks.size() > 0) {
			nodeCode = tasks.get(0).getName();
		}
		final String nodec = nodeCode;		//lambda需要一个final类型的对象
		List<ProNode> filterNodes = nodes.parallelStream().filter(e->e.getNodeName().equals(nodec)).collect(Collectors.toList());
		if(filterNodes!=null && filterNodes.size() > 0) {
			ProNode proNode = filterNodes.get(0);
			tm.setAllowHang(proNode.getAllowHang());
			tm.setAllowStop(proNode.getAllowStop());
			tm.setAllowRecall(proNode.getAllowRecall());
		}
		return tm;
	}

	/**
	 * 根据1个任务id获取 该任务对应的前端TaskModel对象
	 * @param taskId
	 * @return
	 */
	@Override
	public TaskModel getTaskByTaskId(String taskId) {
		Task task = actManagerService.getSingleTaskByTaskId(taskId);
		TaskModel taskModel = new TaskModel();
		taskModel.setInstanceId(task.getProcessInstanceId());
		taskModel.setTaskId(task.getId());
		taskModel.setTaskName(task.getName());
		ProInstance p = proInstanceMapper.selectByInstanceId(task.getProcessInstanceId());
		taskModel.setStartUser(p.getApplyUser());
		taskModel.setFlowRemark(p.getRemark());
		taskModel.setProcessName(p.getProNameStr());
		return taskModel;
	}

	/**
	 *  根据1个任务id获取 该任务之前的上一个任务节点对应的前端TaskModel对象
	 * @param taskId
	 * @return
	 */
	@Override
	public TaskModel getBeforTaskByTaskId(String taskId) {
		Task task = actManagerService.getSingleTaskByTaskId(taskId);
		ProInstance pro=proInstanceMapper.selectByInstanceId(task.getProcessInstanceId());
		TaskModel taskModel = new TaskModel();
		List<InstanceHistory> list = getHistory(task.getProcessInstanceId()).parallelStream().filter(e->FlowNode.HISTORY_LEVEL_A.equals(e.getLevel())).collect(Collectors.toList());
		if(CommonUtil.isNotEmpty(list)){
			taskModel.setInstanceId(list.get(0).getInstanceId());
			taskModel.setTaskName( list.get(0).getWrappedTaskLogicalName() );
			taskModel.setStartUser(pro.getApplyUser());
			taskModel.setLastCompleteTime(list.get(0).getCreateDateStr());
			taskModel.setCompleteUser(list.get(0).getCompleteUser());
			taskModel.setCompleteRemark(list.get(0).getActualRemark());
			taskModel.setProcessName(list.get(0).getProNameStr());
		}
		ProInstance p = proInstanceMapper.selectByInstanceId(task.getProcessInstanceId());
		taskModel.setFlowRemark(p.getRemark());
		return taskModel;
	}

	/**
	 * 根据1个流程实例id 获取工作流引擎的xml字符串
	 * @param instanceId
	 * @return
	 */
	@Override
	public String getBapBpmnByInstanceId(String instanceId) {
		return processMapper.getBapBpmnByInstanceId(instanceId);
	}

	/**
	 * 根据1个流程实例id 获取该实例当前执行的历史记录
	 * @param instanceId
	 * @return
	 */
	@Override
	public List<InstanceHistory> getHistory(String instanceId) {
		List<InstanceHistory> list = instanceHistoryMapper.selectByInstanceIdOderByDesc(instanceId);
		for (InstanceHistory instanceHistory : list) {
			instanceHistory.setTaskName(instanceHistory.getWrappedTaskLogicalName());
			instanceHistory.setRemark( instanceHistory.getActualRemark() );
			instanceHistory.setCreateDateStr(CommonUtil.getymdhms(instanceHistory.getCreateDate()));
		}
		return list;
	}

	/**
	 * 通过实例id 激活一个被挂起(暂停的)实例
	 * @param instanceId
	 */
	@Override
	public void activateInstance(String instanceId) {
		ProInstance p = proInstanceMapper.selectByInstanceId(instanceId);
		p.setState(StateType.APPROVAL_RUN.statusCode());
		proInstanceMapper.update(p);
		actManagerService.activateInstance(instanceId);
		//向'instance_history'插入一条记录
		InstanceHistory history = new InstanceHistory();
		history.setId(CommonUtil.getUuid());
		history.setProcessId(p.getProcessId());
		history.setInstanceId(p.getInstanceId());
		history.setTaskName( FlowNode.ACTIVATION );
		history.setCompleteUser(UserUtils.currentUserInfo().getName());
		history.setCompleteState("1");
		history.setLevel(FlowNode.HISTORY_LEVEL_B);
		history.setRemark("该流程已被发起人激活");
		history.setCreateDate( new Date() );
		instanceHistoryMapper.insert(history);
	}

	/**
	 * 通过实例id 挂起(暂停)1个进行中的流程实例
	 * @param instanceId
	 */
	@Override
	public void suspendInstance(String instanceId) {
		ProInstance p = proInstanceMapper.selectByInstanceId(instanceId);
		p.setState(StateType.STOP.statusCode());
		proInstanceMapper.update(p);
		actManagerService.suspendInstance(instanceId);
		//向'instance_history'插入一条记录
		InstanceHistory history = new InstanceHistory();
		history.setId(CommonUtil.getUuid());
		history.setProcessId(p.getProcessId());
		history.setInstanceId(p.getInstanceId());
		history.setTaskName( FlowNode.SUSPENSION );
		history.setCompleteUser(UserUtils.currentUserInfo().getName());
		history.setCompleteState("1");
		history.setLevel(FlowNode.HISTORY_LEVEL_B);
		history.setCreateDate( new Date() );
		history.setRemark("该流程已被发起人挂起");
		instanceHistoryMapper.insert(history);
	}


	/**
	 * 执行一个任务节点
	 * @param completeTaskRequest	 一个任务完成请求体
	 * @param userId								流程发起人
	 * @param approverId					审批人id(作为第三方的时候，审批人不会作从session中获取，而是远程调用传参过来)
	 * @return
	 */
	@Transactional
	@Override
	public boolean useCompleteTask(CompleteTaskRequest completeTaskRequest , String userId ,String approverId) {
		Map<String, Object> params = null;
		List<Task> childTasks = actManagerService.getTasksByTaskId(completeTaskRequest.getTaskId());
		Task task = childTasks.parallelStream().filter(e->e.getId().equals( completeTaskRequest.getTaskId() )).collect(Collectors.toList()).get(0);
		ProNode node = getProNodeOfCurrentTask(task.getProcessDefinitionId(), task.getProcessInstanceId() , task );
		//如果该节点存在接口调用,那么则执行接口方法,并将结果参数放进activiti中执行流程
		if(node.getFunctionId() != null) {
			String execUrl = functionService.selectById(node.getFunctionId()).getExecUrl();
			params = HttpUtil.getToMap(execUrl);
		}
		//如果是审批节点，需要的额外处理
		if(completeTaskRequest.getIsAgree() != null) {	//TODO:如果isAgree该字段不为空,则表示该任务为审批任务,并且该字段的值即为审批结果
				//这里审批的内部流程变量 是直接根据节点名拼接成的,因此没有从 'pro_paramter'表中去 获取
				examineProcess(completeTaskRequest, node , approverId);
				return true;
		}
			//一般处理
			if(CommonUtil.isEmpty(task)) return false;	//非空检查
			//获取流程实例
			ProInstance p = proInstanceMapper.selectByInstanceId(task.getProcessInstanceId());
			//需要先检查是否最后一个任务节点，在办理任务
			//boolean lastTask = actManagerService.lastTask(task);

			actManagerService.complete(task.getId(), userId!=null?(UserUtils.currentUserInfo().getEmpId()+""):userId , params);
			//添加历史
			addInstanceHistory(p, task, completeTaskRequest , node);
			//检查多任务节点下的子任务是否全部完成，若全部完成，则向'instance_history'插入一条结果记录
			checkMultiTaskIsFinished(childTasks, p, node, task, completeTaskRequest);
			return true;
	}
	
	/**
	 * 【私有方法】【useCompleteTask(CompleteTaskRequest completeTaskRequest) 】会调用此方法
	 * 检查多任务节点下的子任务是否全部完成，若全部完成，则向'instance_history'插入一条结果记录
	 * @param childTasks
	 * @param p
	 * @param node
	 * @param task
	 * @param completeTaskRequest
	 */
	private void checkMultiTaskIsFinished(List<Task> childTasks , ProInstance p, ProNode node, Task task , CompleteTaskRequest completeTaskRequest) {
		if(completeTaskRequest.getTaskType().equals(FlowNode.MULTI_TASK_CHILD)) {
			//如果是多任务的子任务，直到该多任务下的子任务全部完成,都能够查询出来其他的子任务
			if(childTasks != null && childTasks.size() == 1) {
				InstanceHistory history = new InstanceHistory();
				history.setId(CommonUtil.getUuid());
				history.setProcessId(p.getProcessId());
				history.setInstanceId(p.getInstanceId());
				history.setTaskName(task.getTaskDefinitionKey().split("-")[0] +"-"+node.getNodeNameBySplit0()+"-"+ node.getNodeNameBySplit()+"-"+FlowNode.MULTI_TASK );
				List<InstanceHistory> rawhistorys = instanceHistoryMapper.selectByInstanceId(task.getProcessInstanceId());
				String completeUsers = rawhistorys.parallelStream().filter(e->e.getTaskLogicalName().equals(node.getNodeNameBySplit0())).map(e->e.getCompleteUser()).collect(Collectors.joining(","));
				history.setCompleteUser(completeUsers);
				history.setCompleteState("1");
				history.setLevel(FlowNode.HISTORY_LEVEL_A);
				history.setRemark(node.getNodeNameBySplit0() + "已通过");
				try {
					Thread.sleep(500);		//TODO:强制睡眠0.5秒以便该条记录能顺利成为 该流程的最新记录
				} catch (InterruptedException e1) {
					e1.printStackTrace();
				}
				history.setCreateDate(  new Date() );
				instanceHistoryMapper.insert(history);
			}
		}
	}
	
	/**
	 * 	【私有方法】【useCompleteTask(CompleteTaskRequest completeTaskRequest) 】会调用此方法
	 *  向业务表'instance_history'中添加数据
	 * @param p				ProInstance对象
	 * @param task			任务
	 * @param completeTaskRequest
	 */
	private  void addInstanceHistory(ProInstance p  , Task task , CompleteTaskRequest completeTaskRequest, ProNode node) {
		//需要向业务表'instance_history'中添加数据
		//添加任务的历史记录
 		InstanceHistory history = new InstanceHistory();
		history.setId(CommonUtil.getUuid());
		history.setProcessId(p.getProcessId());
		history.setInstanceId(p.getInstanceId());
		history.setTaskName(task.getTaskDefinitionKey().split("-")[0] +"-"+node.getNodeNameBySplit0()+"-"+ task.getName()+"-"+node.getTaskType() );
		history.setCompleteUser(StringUtils.isBlank(completeTaskRequest.getExecutorName())?UserUtils.currentUserInfo().getName():completeTaskRequest.getExecutorName() );
		history.setCompleteState("1");
		history.setLevel(FlowNode.HISTORY_LEVEL_A);
		history.setCreateDate( new Date() );
		if ( StringUtils.isNotBlank(completeTaskRequest.getIsAgree()) ){
			//【1】然后记录 审批结果 相关信息
			switch (completeTaskRequest.getIsAgree()) {
			case "1":
				if(StringUtils.isBlank(completeTaskRequest.getDealSuggestion()) ) {
					history.setRemark(FlowNode.EXAMINE_AGREE+"-1");
				}else {
					history.setRemark(completeTaskRequest.getDealSuggestion()+"-1");
				}	
				break;
			case "0":
				if(StringUtils.isBlank(completeTaskRequest.getDealSuggestion()) ) {
					history.setRemark(FlowNode.EXAMINE_REJECT+"-0");
				}else {
					history.setRemark(completeTaskRequest.getDealSuggestion()+"-0");
				}
				break;
			default:break;
			}
		}else {
			 String remark = completeTaskRequest.getDealSuggestion();
			history.setRemark( remark);
		}

		// 【2】最后记录 流程执行顺序，对该流程的执行顺序进行归档
		instanceHistoryMapper.insert(history);
		//如果处理完任务后 整个流程结束了,那么还要添加结束节点的历史记录
		boolean lastTask = ifFinishedAndDo(task.getProcessInstanceId() , completeTaskRequest , task);

		if(lastTask){
			//添加历史
			InstanceHistory lastHistory = new InstanceHistory();
			lastHistory.setId(CommonUtil.getUuid());
			lastHistory.setProcessId(p.getProcessId());
			lastHistory.setInstanceId(p.getInstanceId());
			lastHistory.setTaskName("流程结束");
			lastHistory.setCompleteUser(UserUtils.currentUserInfo().getName());
			lastHistory.setCompleteState("1");
			lastHistory.setLevel(FlowNode.HISTORY_LEVEL_A);
			lastHistory.setCreateDate( new Date( new Date().getTime()+1000L ));		//流程结束时间向后+1s
			lastHistory.setRemark("该流程结束");
			instanceHistoryMapper.insert(lastHistory);
			//如果不是多任务子任务，则强制结束该流程实例
			List<Task> tasks = actManagerService.getTaskByInstanceId(task.getProcessInstanceId());
			if(tasks.size()>0 && tasks.get(0).getTaskDefinitionKey().split("-").length < 2) {	
				actManagerService.endActiviti(task.getProcessInstanceId());
			}
		}
	}

	/**
	 * 【私有方法】【addInstanceHistory(...)调用此方法】查询一个流程实例是否结束
	 * @param processInstanceId
	 * @return
	 */
	private boolean ifFinishedAndDo(String processInstanceId , CompleteTaskRequest completeTaskRequest , Task task) {
		String startUserId = processMapper.isFinished(processInstanceId);
		if(startUserId != null){
			//【3】如果该流程结束了，那么找到最后一个TaskId，找到签收人，他就是“审批人”,找到后并调用回调接口
			if( StringUtils.isNoneBlank( completeTaskRequest.getIsAgree() )){
				//审批任务id
				String taskId = task.getId();
				//获得审批节点的审批人id
				String approverId = processMapper.selectSigner(taskId);
				String apprCallBackUrl = getApprCallBackUrl(Integer.valueOf(processInstanceId));
				apprCallBackUrl = apprCallBackUrl.indexOf("http://")!=-1?apprCallBackUrl:"http://"+apprCallBackUrl;
				completeTaskRequest.getIsAgree();
				HashMap<String,String> params = new HashMap<>();
				params.put("instanceId",processInstanceId);
				params.put("startUserId",processMapper.selectStartUserId(task.getProcessInstanceId()));
				params.put("approverId",approverId);
				params.put("reviewResult",completeTaskRequest.getIsAgree());
				OkHttpUtil.post( apprCallBackUrl , params);
				System.out.println(String.format("流程(%s)已结束",processInstanceId));
				//然后将该流程实例的state状态置为已结束
				proInstanceMapper.finishInstace(task.getProcessInstanceId());
				return true;
			}
		}
		return false;
	}

	/**
	 * 根据 流程实例id 和 节点id 获取对应节点的nodeCode(什么是nodeCode?)
	 * nodeCode大致为这样: 比如 demo_node_3、 demo_node_1 、可以查看'process'表中的'bap_bpmn'中的json数据
	 * @param instanceId
	 * @param nodeId
	 * @return
	 */
	@Override
	public String getNodeCode(String instanceId , String nodeId) {
		List<Task> list = actManagerService.getTaskByInstanceId(instanceId);
		if(CommonUtil.isNotEmpty(list)){
			String nodeCode = list.get(0).getTaskDefinitionKey();
	        if(nodeCode != null) {
	        	//TODO:解析出对应的节点id，子任务id大致为这样: demo_node_3-123456789，而一般的单任务节点为demo_node_3
	        	return nodeCode.split("-")[0];	
	        }
	        return nodeId==null?null:nodeId;
		}
		return null;
	}
	
	/**
	 * 设置 审批节点的内部 审批结果变量
	 * @return
	 */
	private String setApprovalVariable(String nodeId) {
		return nodeId + "_result";		//不能用- + * /等符号 ，因为在表达式中这些符号是有意义的!
	}
	
	/**
	 * 处理审批节点
	 * @param completeTaskRequest
	 */
	private void examineProcess( CompleteTaskRequest completeTaskRequest , ProNode node ,String userId) {
		//【1】传入 审批的结果 同意or不同意
		Map<String,Object> params =  new HashMap<>();
		params.put( setApprovalVariable(node.getNodeId()) , Integer.valueOf( completeTaskRequest.getIsAgree() ) );	
		StringBuilder childTaskName = new StringBuilder(node.getNodeId());
		if("1".equals( completeTaskRequest.getIsAgree()  )) {
			childTaskName.append("-allowTask");
		}else {
			childTaskName.append("-rejectTask");
		}
		List<Task> tasks = actManagerService.getTasksByTaskId(completeTaskRequest.getTaskId());
		//一般tasks的只会有2个
		//【2】把两个 同意任务 和 拒绝任务 都提交了，并且向历史记录中添加  用户点击的那个任务
		for (Task task : tasks) {
			//处理任务
			actManagerService.complete(task.getId(), StringUtils.isBlank(userId)?UserUtils.currentUserInfo().getEmpId()+"":userId, params);
			if(CommonUtil.isEmpty(task) || !task.getTaskDefinitionKey().equals(childTaskName.toString())) continue;	//非空检查和 同意和驳回 只有一个被插入到历史任务记录表中
			//获取流程实例
			ProInstance p = proInstanceMapper.selectByInstanceId(task.getProcessInstanceId());
			//向业务表'instance_history'中添加数据
			addInstanceHistory(p,task,completeTaskRequest ,node);
		}	
	}
	
	/**
	 * 根据流程实例id 终止并结束该流程实例
	 * @param instanceId
	 */
	@Override
	public void endActiviti(String instanceId) {
		ProInstance p = proInstanceMapper.selectByInstanceId(instanceId);
		p.setState("9");
		actManagerService.endActiviti(instanceId);
		InstanceHistory history = new InstanceHistory();
		history.setId(CommonUtil.getUuid());
		history.setProcessId(p.getProcessId());
		history.setInstanceId(p.getInstanceId());
		history.setTaskName( FlowNode.TERMINATION );
		history.setCompleteUser(UserUtils.currentUserInfo().getName());
		history.setCompleteState("1");
		history.setCreateDate( new Date() );
		history.setLevel(FlowNode.HISTORY_LEVEL_B);
		history.setRemark("流程已被发起人终止");
		instanceHistoryMapper.insert(history);
		proInstanceMapper.update(p);
	}

	/**
	 * 【如果是以审批任务结束】，那么获取到审批结果
	 * @param instanceId
	 	public boolean getExamineResult(String instanceId){
		List<InstanceHistory> instanceHistories = instanceHistoryMapper.selectByInstanceId(instanceId);
		try {
			InstanceHistory examine = instanceHistories.stream().filter(e -> e.getTaskName().contains(FlowNode.EXAMINE)).collect(Collectors.toList()).get(0);
			//1表示 审批通过
			return "1".equals(  examine.getRemark().split("-")[1] )?true:false;
		}catch (Exception e){
			System.err.println("该实例[instanceId="+instanceId+"] 不存在审批节点，已默认返回false");
			return false;
		}
	}
	*/


	/**
	 * 如果存在审批回调接口，则从流程变量中获取该接口地址
	 * @param instanceId 流程实例id
	 * @return	String
	 */
	@Override
	public String getApprCallBackUrl(Integer instanceId) {
		String reviewCallBack = processMapper.selectVariableByNameAndIstanceId(instanceId+"", "reviewCallBack");
		System.out.println("回调地址是: " + reviewCallBack);
		return reviewCallBack;
	}

	/**
	 * 如果存在获取 "modelId"，用于查看页面或者编辑页面
	 * @param instanceId 流程实例id
	 * @return	String

	@Override
	public String getModelId(Integer instanceId) {
		String reviewCallBack = processMapper.selectVariableByNameAndIstanceId(instanceId+"", "modelId");
		return reviewCallBack;
	}
	 */

	/**
	 * 获取一个流程实例的所有 流程变量，以Map形式返回
	 * @param instanceId 流程实例id
	 * @return
	 */
	@Override
	public Map<String, String> getVariables(String instanceId) {
		List<FlowVariable> variableListist = processMapper.selectVariablesByInstanceId(instanceId);
		return FlowVariable.toMap(variableListist);
	}

	/**
	 * 撤回一个流程实例的当前任务节点 到 上一个执行过的任务节点
	 * @param instanceId		流程实例id
	 * @param taskName		任务名，如果不传入，使用默认任务名"[未知]"
	 * @return
	 */
	public CancelTask revokeInstance(String instanceId, String taskName) {
		taskName = StringUtils.isBlank(taskName)?"[未知]":taskName;
		CancelTask result =flowTraceService.useCancelTask(instanceId, taskName);
		ProInstance p = proInstanceMapper.selectByInstanceId(instanceId);
		InstanceHistory history = new InstanceHistory();
		history.setId(CommonUtil.getUuid());
		history.setProcessId(p.getProcessId());
		history.setInstanceId(p.getInstanceId());
		history.setTaskName( FlowNode.REVOCATION );
		history.setCompleteUser(UserUtils.currentUserInfo().getName());
		history.setCompleteState("1");
		history.setCreateDate( new Date() );
		history.setLevel(FlowNode.HISTORY_LEVEL_B);
		history.setRemark("已撤回到节点'" +taskName+"'");
		instanceHistoryMapper.insert(history);
	
		return result;
	}

	/**
	 * 设置替换后的的url
	 * @param rawUrl
	 * @param instanceId
	 * @return
	 */
	public String setPlaceHolderViewPage(String rawUrl , String instanceId){
		return StringConventor.placeHolder(
				rawUrl,
				Constant.VIEWPAGE_PREFIX,
				Constant.VIEWPAGE_SUFFIX,
				FlowVariable.toMap(processMapper.selectVariablesByInstanceId(instanceId)));
	}
}
